/*
 * Copyright 2009 Nordic Consulting & Development Company
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package pl.ncdc.web.login.client.ui;

import pl.ncdc.web.login.client.Credentials;
import pl.ncdc.web.login.client.User;
import pl.ncdc.web.login.client.i18n.LoginConstants;
import pl.ncdc.web.login.client.service.AuthenticationResult;
import pl.ncdc.web.login.client.service.AuthenticationServiceAsync;
import pl.ncdc.web.login.client.service.UserServiceAsync;
import pl.ncdc.web.login.client.ui.NotificationAreaWidget.State;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 *
 * <p>
 * Created on Jul 7, 2008
 *
 * @author hshsce
 * @version $Id: LoginWidget.java 204 2009-06-03 15:34:41Z hshsce $
 */
public class LoginWidget extends Composite {

	public static final String WIDGET_ID = "ncdc-web-login-LoginWidget";

	public static final String REMEMBER_ME_CHECK_BOX_ID = "RememberMeCheckBox";

	public static final String TITLE_ID = "Title";

	public static final LoginConstants CONSTANTS = GWT.create(LoginConstants.class);

	private final VerticalPanel m_panel = new VerticalPanel();
	{
		m_panel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
		m_panel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
	}

	private final AuthenticationListenerCollection m_listeners = new AuthenticationListenerCollection();

	private Widget m_titleWidget;

	private AttributesManager m_attributesManager;

	private final NotificationAreaWidget m_notificationArea;

	private CheckBox m_rememberMeCheckBox;

	private UserListWidget m_userList;

	private UserSelectionListener m_userListUserSelectionListener;

	private CredentialsListener m_userListCredentialsListener;

	private final UserCredentialsWidget m_userCredentials;

	private final UserServiceAsync m_userService;

	private final AuthenticationServiceAsync m_authService;

	public LoginWidget(final AuthenticationServiceAsync authService, final UserServiceAsync userService) {
		this(null, null, authService, userService);
	}

	public LoginWidget(final String title, final String imgPath, final String imgExtension, final AuthenticationServiceAsync authService, final UserServiceAsync userService) {
		this(new Label(title) {{ setStyleName(TITLE_ID); }}, imgPath, imgExtension, authService, userService);
	}

	public LoginWidget(final Widget titleWidget, final String imgPath, final String imgExtension, final AuthenticationServiceAsync authService, final UserServiceAsync userService) {
		this(titleWidget, new DefaultUserWidgetFactory(imgPath, imgExtension), authService, userService);
	}

	public LoginWidget(final Widget title, final UserWidgetFactory factory, final AuthenticationServiceAsync authService, final UserServiceAsync userService) {
		if (authService == null) {
			throw new IllegalArgumentException("authService cannot be null");
		}
		if (userService == null) {
			throw new IllegalArgumentException("userService cannot be null");
		}
		m_userService = userService;
		m_authService = authService;
		setTitleWidget(title);
		m_notificationArea = new NotificationAreaWidget();
		final SuggestOracle oracle = new UidSuggestOracle(m_userService);
		m_userCredentials = new UserCredentialsWidget(oracle);
		m_userCredentials.addCredentialsListener(new CredentialsListenerAdapter() {
			@Override
			public void onUidChangeAttempt() {
				m_notificationArea.setState(State.CLEAR);
			}
			@Override
			public void onCredentialsProvided(final String uid, final String password) {
				prepareAuthentication(uid);
				authenticate(uid, password, getRememberMeOption());
			}
		});
		m_panel.add(m_notificationArea);
		m_panel.add(m_userCredentials);
		setUserWidgetFactory(factory);
		setRememberMeEnabled(true);
		initWidget(m_panel);
		setStyleName(WIDGET_ID);
	}

	public void setTitleWidget(final Widget widget) {
		if (widget == null) {
			if (m_titleWidget != null) {
				m_panel.remove(0);
			}
		} else {
			if (m_titleWidget != null) {
				m_titleWidget.removeFromParent();
			}
			m_panel.insert(widget, 0);
		}
		m_titleWidget = widget;
	}

	public void setRememberMeEnabled(final boolean enabled) {
		if (enabled) {
			if (m_rememberMeCheckBox == null) {
				m_rememberMeCheckBox = new CheckBox(CONSTANTS.rememberMe());
				m_rememberMeCheckBox.setStyleName(REMEMBER_ME_CHECK_BOX_ID);
			} else {
				m_rememberMeCheckBox.removeFromParent();
			}
			m_panel.add(m_rememberMeCheckBox); //at the end
		} else {
			m_rememberMeCheckBox.removeFromParent();
			m_rememberMeCheckBox = null;
		}
	}

	public void setAttributesManager(final AttributesManager manager) {
		if (m_attributesManager != null) {
			m_attributesManager.getWidget().removeFromParent();
			m_attributesManager = null;
		}
		m_attributesManager = manager;
		m_panel.insert(m_attributesManager.getWidget(), m_panel.getWidgetCount() - 1); //TODO check if this is valid index
	}

	public void setUserWidgetFactory(final UserWidgetFactory factory) {
		if (m_userList != null) {
			m_userList.removeFromParent();
			m_userList.removeUserSelectionListener(m_userListUserSelectionListener);
			m_userCredentials.removeCredentialsListener(m_userListCredentialsListener);
			m_userList = null;
		}
		if (factory != null) {
			m_userList = newUserListWidget(factory);
			int beforeIndex;
			if (m_titleWidget == null) {
				beforeIndex = 0;
			} else {
				beforeIndex = 1;
			}
			m_panel.insert(m_userList, beforeIndex);
		}
	}

	public void addAuthenticationListener(final AuthenticationListener listener) {
		m_listeners.add(listener);
	}

	public void removeAuthenticationListener(final AuthenticationListener listener) {
		m_listeners.remove(listener);
	}

	private UserListWidget newUserListWidget(final UserWidgetFactory factory) {
		final UserListWidget userList = new UserListWidget(factory);
		m_userService.list(new AsyncCallback<User[]>() {
			public void onSuccess(final User[] users) {
				userList.setUsers(users);
			}
			public void onFailure(final Throwable caught) {
				Window.alert(CONSTANTS.serverCommunicationError());
			}
		});
		m_userListUserSelectionListener = new UserSelectionListener() {
			public void onUserSelected(final String uid) {
				if (m_notificationArea != null) {
					m_notificationArea.setState(State.CLEAR);
				}
				m_userCredentials.setUid(uid);
			}
		};
		userList.addUserSelectionListener(m_userListUserSelectionListener);
		m_userListCredentialsListener = new CredentialsListenerAdapter() {
			@Override
			public void onUidProvided(final String uid) {
				userList.selectUser(uid);
			}
		};
		m_userCredentials.addCredentialsListener(m_userListCredentialsListener);
		return userList;
	}

	private void authenticate(final String uid, final String password, final boolean rememberMe) {
		final Credentials c = new Credentials(uid, password, rememberMe);
		if (m_attributesManager != null) {
			c.setAttributes(m_attributesManager.getAttributes());
		}
		m_authService.login(c, new AsyncCallback<AuthenticationResult>() {
			public void onSuccess(final AuthenticationResult result) {
				handleAuthenticationResult(uid, result);
			}
			public void onFailure(final Throwable caught) {
				Window.alert(CONSTANTS.serverCommunicationError());
				resetWidgets();
			}
		});
	}

	private void handleAuthenticationResult(final String uid, final AuthenticationResult result) {
		switch (result) {
		case OK: //do nothing listeners will be informed
			break;
		case INVALID_UID:
			handleError(CONSTANTS.invalidUsername());
			break;
		case INVALID_PASSWORD:
			handleError(CONSTANTS.invalidPassword());
			break;
		default:
			throw new AssertionError("Unknown AuthenticationResult: " + result);
		}
		m_listeners.fireAuthenticationResponse(uid, result);
	}

	private void prepareAuthentication(final String uid) {
		if (m_notificationArea != null) {
			m_notificationArea.setState(State.BUSY);
		}
		if (m_rememberMeCheckBox != null) {
			m_rememberMeCheckBox.setEnabled(false);
		}
		if (m_userList != null) {
			m_userList.setEnabled(false);
			m_userList.selectUser(uid);
		}
		m_userCredentials.setEnabled(false);
	}

	private void resetWidgets() {
		if (m_notificationArea != null) {
			m_notificationArea.setState(State.CLEAR);
		}
		if (m_rememberMeCheckBox != null) {
			m_rememberMeCheckBox.setEnabled(true);
		}
		m_userCredentials.reset();
		m_userCredentials.setEnabled(true);
	}

	private void handleError(final String message) {
		if (m_notificationArea != null) {
			m_notificationArea.setMessage(message);
			m_notificationArea.setState(State.NOTIFICATION);
		}
		if (m_rememberMeCheckBox != null) {
			m_rememberMeCheckBox.setEnabled(true);
		}
		if (m_userList != null) {
			m_userList.setEnabled(true);
		}
		m_userCredentials.showUidPrompt();
		m_userCredentials.setEnabled(true);
		m_userCredentials.setFocus(true);
	}

	private boolean getRememberMeOption() {
		final boolean result;
		if (m_rememberMeCheckBox.getParent() != null) {
			result = true;
		} else {
			result = m_rememberMeCheckBox.isChecked();
		}
		return result;
	}

}
